// Package jwts 提供与 JWT (JSON Web Token) 相关的功能，用于用户认证和授权。
// 它包含使用自定义声明（claims）生成和解析 JWT 令牌的函数。
package jwts

import (
	"errors"
	"time"

	"github.com/golang-jwt/jwt/v4"
)

// JwtPayLoad 代表存储在 JWT 载荷（payload）中的自定义数据。
// 此结构体包含将嵌入令牌中的用户特定信息。
// UserID: 用户的唯一标识符
// Username: 用于显示的用户名
// Role: 用户的角色/权限级别 (1 表示普通用户, 2 表示管理员)
type JwtPayLoad struct {
	UserID   uint   `json:"user_id"`  // 用户唯一标识符
	Username string `json:"username"` // 用户名，用于显示
	Role     int    `json:"role"`     // 用户角色/权限级别  1 普通用户  2 管理员
}

// CustomClaims 使用我们的自定义载荷扩展了标准的 JWT 声明（claims）。
// 此结构体将已注册的 JWT 声明（如过期时间）与我们的自定义用户数据结合在一起。
// JwtPayLoad: 嵌入令牌中的自定义用户数据
// RegisteredClaims: RFC 7519 中定义的标准 JWT 声明
type CustomClaims struct {
	JwtPayLoad           // 嵌入自定义的用户数据
	jwt.RegisteredClaims // 嵌入标准JWT声明（如过期时间等）
}

// GenToken 使用提供的用户信息和设置生成一个新的 JWT 令牌。
// 此函数创建一个已签名的 JWT 令牌，可用于用户认证。
//
// 参数:
// user: 包含要嵌入令牌的用户信息的 JwtPayLoad 结构体
// accessSecret: 用于对令牌签名的密钥（应妥善保管）
// expires: 令牌过期时间（单位：小时）
//
// 返回值:
// string: 已签名的 JWT 令牌字符串
// error: 令牌生成过程中发生的任何错误
func GenToken(user JwtPayLoad, accessSecret string, expires int64) (string, error) {
	// 使用用户数据和标准声明创建自定义声明
	claim := CustomClaims{
		JwtPayLoad: user,
		RegisteredClaims: jwt.RegisteredClaims{
			// 设置令牌过期时间：当前时间 + 指定的持续时间
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(expires))),
		},
	}

	// 使用 HS256 签名方法和自定义声明创建一个新令牌
	// HS256 是一种对称签名算法，使用相同的密钥进行签名和验证
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)

	// 使用提供的密钥对令牌进行签名，并返回签名后的令牌字符串
	// 密钥按签名方法要求转换为字节切片
	return token.SignedString([]byte(accessSecret))
}

// ParseToken 解析并验证一个 JWT 令牌字符串。
// 此函数验证令牌的签名并检查其有效性（包括是否过期）。
//
// 参数:
// tokenStr: 待解析和验证的 JWT 令牌字符串
// accessSecret: 用于验证令牌签名的密钥（必须与签名密钥匹配）
// expires: 令牌过期时间（单位：小时）（用于验证上下文）
//
// 返回值:
// *CustomClaims: 如果令牌有效，则指向已解析声明的指针
// error: 解析或验证过程中发生的任何错误（包括过期）
func ParseToken(tokenStr string, accessSecret string, expires int64) (*CustomClaims, error) {
	// 使用自定义声明和一个密钥函数解析令牌字符串
	// 密钥函数返回用于签名验证的密钥
	token, err := jwt.ParseWithClaims(tokenStr, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名方法是否为 HS256（出于安全考虑）
		// 这可以防止使用较弱算法带来的漏洞
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, errors.New("非预期的签名方法")
		}
		// 返回用于签名验证的密钥（转换为字节切片）
		return []byte(accessSecret), nil
	})

	// 如果解析失败，返回错误
	if err != nil {
		return nil, err
	}

	// 检查令牌是否有效以及声明是否能转换为 CustomClaims 类型
	// token.Valid 确保签名正确且令牌未被篡改
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		// 返回已解析的声明
		return claims, nil
	}

	// 如果令牌无效或声明无法转换，返回错误
	return nil, errors.New("无效令牌")
}
